/***************************************************************************
 *            screen.c
 *
 *  Mon Aug 25 16:38:05 2008
 *  Copyright  2008  Tiago Falcão
 *  <tiago@tiagofalcao.com>
 ****************************************************************************/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */
#include "screen.h"

objects_3d * objs;

//Inicialização da Tela
void screen(int x,int y,int w,int h,char * title){
	//Objetos
	objs=objs3d_create();
	
	//Double Buffer, RGBA e Depth
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	//Tamanho
	glutInitWindowSize (w, h);
	//Posição
	glutInitWindowPosition (x, y);
	//Cria Janela e seta Titulo
	glutCreateWindow (title);
    // Especifica que a cor de fundo da janela será preta
	glClearColor(0.0, 0.0, 0.0, 1.0);
	//Seta função de desenho
	glutDisplayFunc (display);
	//Inicializa Camera
	cam_init();
	//Seta função de redimensionamento
	glutReshapeFunc (reshape_listener);
	//Listener do Teclado
	glutKeyboardFunc(key_listener);
	glutSpecialFunc(special_key_listener);
	//Listener do Mouse
	glutMouseFunc(mouse_listener);
	//Listener do Scroll do Mouse
	glutMouseWheelFunc(mouse_wheel_listener);
	//Cria Menu
	create_menu();
	//Close Fuction
	glutWMCloseFunc(end_program);
	//Show
	glutMainLoop ();
}

//Funcao de Desenho
void display(){
	//Limpeza
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	//Luz
	setup_light();
	
	//Desenha Objetos 3D
	objs3d_display(objs);

	//Troca Buffers
	glutSwapBuffers();
}

//Listener do Tamanho da Janela
void reshape_listener (GLsizei w, GLsizei h) {
	// Para previnir uma divisão por zero
	if ( h == 0 ) h = 1;

	// Especifica o tamanho da viewport
	glViewport(0, 0, w, h);
	
	// Calcula a correção de aspecto
	cam_aspect((GLfloat)w/(GLfloat)h);
	
	//Reconfigura Camera
	cam_setup();
}

//Listener do Mouse
void mouse_listener(int button,int state,int x,int y){
}

//Listener do Scroll do Mouse
void mouse_wheel_listener(int wheel_number,int direction,int x,int y){
	cam_zoom(direction);
	cam_setup();
	glutPostRedisplay();
}

//Listener do Teclado
void key_listener(unsigned char key,int x,int y){
	if(! objs->selected){							//CAMERA
		switch(key){
			case GLUT_KEY_ESC:					//Sai do programa
				exit(0);
			case '8':							//Rotaciona para cima
				cam_rot_phi (-1);break;
			case '2':							//Rotaciona para baixo
				cam_rot_phi (1);break;
			case '4':							//Rotaciona para esquerda
				cam_rot_theta (-1);break;
			case '6':							//Rotaciona para direita
				cam_rot_theta (1);break;
			case '+':							//Afasta Camera
				cam_zoom(1);break;
			case '-':							//Aproxima Camera
				cam_zoom(-1);break;
		}
		//Reconfigura Camera
		cam_setup();
	}else{										//OBJETO
		switch(key){
			case GLUT_KEY_ESC:					//Desmarca objeto
				objs3d_unselect(objs);
				break;
			case '8':							//Rotaciona para cima
				obj3d_rot_x(objs->selected,-1);break;
			case '2':							//Rotaciona para baixo
				obj3d_rot_x(objs->selected,1);break;
			case '4':							//Rotaciona para esquerda
				obj3d_rot_y(objs->selected,-1);break;
			case '6':							//Rotaciona para direita
				obj3d_rot_y(objs->selected,1);break;
			case '9':							//Rotaciona anti-horario
				obj3d_rot_z(objs->selected,1);break;
			case '3':							//Rotaciona horario
				obj3d_rot_z(objs->selected,-1);break;
			case '+':							//Aumenta Tamanho
				obj3d_size(objs->selected,1);break;
			case '-':							// Reduz Tamanho
				obj3d_size(objs->selected,-1);break;
			case 'q':							// Scala x +
				obj3d_scale_x(objs->selected,1);break;
			case 'a':							// Scala x -
				obj3d_scale_x(objs->selected,-1);break;
			case 'w':							// Scala y +
				obj3d_scale_y(objs->selected,1);break;
			case 's':							// Scala y -
				obj3d_scale_y(objs->selected,-1);break;
			case 'e':							// Scala z +
				obj3d_scale_z(objs->selected,1);break;
			case 'd':							// Scala z -
				obj3d_scale_z(objs->selected,-1);break;
			case 'z':							// Modo
				objs->selected->mode=OBJMODEL_MODE_WIRE;break;
			case 'x':							// Modo
				objs->selected->mode=OBJMODEL_MODE_FILL;break;
			case 'c':							// Modo
				objs->selected->mode=OBJMODEL_MODE_WIRE | OBJMODEL_MODE_FILL;break;
			case 'v':							// Modo
				objs->selected->mode=OBJMODEL_MODE_SMOOTH | OBJMODEL_MODE_FILL;break;
			case 'b':							// Modo
				objs->selected->mode=OBJMODEL_MODE_FACE;break;
			case 'n':							// Modo
				objs->selected->mode=OBJMODEL_MODE_SMOOTH | OBJMODEL_MODE_FACE | OBJMODEL_MODE_FILL;break;
		}
	}//ELSE

	//Redisplay
	glutPostRedisplay();
}

//Listener de Teclas Especiais
void special_key_listener(int key,int x,int y){
	if(! objs->selected){							//CAMERA
		switch(key){
			case GLUT_KEY_F2:					//Seleciona Objeto Anterior
				objs3d_select_prev(objs);
				break;
			case GLUT_KEY_F3:					//Seleciona Proximo Objeto
				objs3d_select_next(objs);
				break;
			case GLUT_KEY_UP:					//Move a Camera para "Cima"
				cam_trans_y (1);break;
			case GLUT_KEY_DOWN:					//Move a Camera para "Baixo"
				cam_trans_y (-1);break;
			case GLUT_KEY_LEFT:					//Move a Camera para "Esquerda"
				cam_trans_x (-1);break;
			case GLUT_KEY_RIGHT:				//Move a Camera para "Direita"
				cam_trans_x (1);break;
			case GLUT_KEY_PAGE_UP:				//Move a Camera para "Tras"
				cam_trans_z (-1);break;
			case GLUT_KEY_PAGE_DOWN:			//Move a Camera para "Frente"
				cam_trans_z (1);break;
		}
		//Reconfigura Camera
		cam_setup();
	}else{										//OBJETO
		switch(key){
			case GLUT_KEY_F2:					//Seleciona Objeto Anterior
				objs3d_select_prev(objs);
				break;
			case GLUT_KEY_F3:					//Seleciona Proximo Objeto
				objs3d_select_next(objs);
				break;
			case GLUT_KEY_END:					//Deleta Objeto
				objs3d_delete_selected(objs);
				break;
			case GLUT_KEY_UP:					//Move o Objeto para "Cima"
				obj3d_trans_y(objs->selected,1);break;
			case GLUT_KEY_DOWN:					//Move o Objeto para "Baixo"
				obj3d_trans_y(objs->selected,-1);break;
			case GLUT_KEY_LEFT:					//Move o Objeto para "Esquerda"
				obj3d_trans_x(objs->selected,-1);break;
			case GLUT_KEY_RIGHT:				//Move o Objeto para "Direita"
				obj3d_trans_x(objs->selected,1);break;
			case GLUT_KEY_PAGE_UP:				//Move o Objeto para "Tras"
				obj3d_trans_z(objs->selected,-1);break;
			case GLUT_KEY_PAGE_DOWN:			//Move o Objeto para "Frente"
				obj3d_trans_z(objs->selected,1);break;
		}
	}
	//Redisplay
	glutPostRedisplay();
}

//Cria Menu
void create_menu(){
	//Submenu
	int obj_menu=glutCreateMenu(submenu_obj_listener);
	glutAddMenuEntry("CubeOBJ", 0);
	glutAddMenuEntry("Cow", 1);
	glutAddMenuEntry("Bunny", 2);
	glutAddMenuEntry("Cube", 3);
	glutAddMenuEntry("Sphere", 4);
	glutAddMenuEntry("Teapot", 5);
	//Menu
	glutCreateMenu(menu_listener);
	glutAddSubMenu("Load Object", obj_menu);
	glutAddMenuEntry("Quit", 1);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
}

//Listener do Menu Principal
void menu_listener(int value){
	if(value==1)							//Finaliza Execucao
		exit(0);
}

//Listener do Submenu de Selecao de Objetos
void submenu_obj_listener(int value){
	object3d * obj;
	switch(value){
		case 0: //Cube em OBJ
			obj=objs3d_add(objs,model_display,model_load("./models/cube.obj"),model_delete);
			obj->mode=OBJMODEL_MODE_SMOOTH | OBJMODEL_MODE_FILL;
			break;
		case 1: //Cow em OBJ
			obj=objs3d_add(objs,model_display,model_load("./models/cow.obj"),model_delete);
			obj->mode=OBJMODEL_MODE_SMOOTH | OBJMODEL_MODE_FILL;
			break;
		case 2: //Bunny em OBJ
			obj=objs3d_add(objs,model_display,model_load("./models/bunny.obj"),model_delete);
			obj->mode=OBJMODEL_MODE_SMOOTH | OBJMODEL_MODE_FILL;
			break;
		case 3: //Cubo com glut
			obj=objs3d_add(objs,simple_cube_display,calloc(1,sizeof(GLfloat)),free);
			*((GLfloat*)obj->object)=1.;
			break;
		case 4: //Esfera com glut
			obj=objs3d_add(objs,simple_sphere_display,calloc(1,sizeof(simple_sphere)),free);
			((simple_sphere*)obj->object)->radius=1.;
			((simple_sphere*)obj->object)->slices=30;
			((simple_sphere*)obj->object)->stacks=30;
			break;
		case 5: //Chaleira com glut
			obj=objs3d_add(objs,simple_teapot_display,calloc(1,sizeof(GLfloat)),free);
			*((GLfloat*)obj->object)=1.;
			break;
	}
	//Definicoes padroes do objeto criado...um pouco de preguica
	obj3d_set_color(obj,.0,1.,.3,1.);
	obj3d_set_ambient(obj,.1,.1,.1,1.);
	obj3d_set_diffuse(obj,1.,1.,1.,1.);
	obj3d_set_specular(obj,1.,1.,1.,1.);
	obj3d_set_emission(obj,.0,.0,.0,1.);
	obj3d_set_shininess(obj,100.);
	//Redisplay
	glutPostRedisplay();
}

//Configura Luz
void setup_light(){
	GLfloat luzAmbiente[4]={.1,.1,.1,1.};		// luz ambiente
	GLfloat luzDifusa[4]={.8,.8,.8,1.};			// "cor" 
	GLfloat luzEspecular[4]={.9, .9, .9, 1.};   // "brilho" 
	GLfloat posicaoLuz[4]={0.0, 100.0, 100.0, 1.}; // posicao da luz
	
	// Habilita o modelo de colorização de Gouraud
	glShadeModel(GL_SMOOTH);

	// Ativa o uso da luz ambiente 
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, luzAmbiente);

	// Define os parâmetros da luz de número 0
	glLightfv(GL_LIGHT0, GL_AMBIENT, luzAmbiente);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, luzDifusa );
	glLightfv(GL_LIGHT0, GL_SPECULAR, luzEspecular );
	glLightfv(GL_LIGHT0, GL_POSITION, posicaoLuz );

	// Habilita a definição da cor do material a partir da cor corrente
	glEnable(GL_COLOR_MATERIAL);
	//Habilita o uso de iluminação
	glEnable(GL_LIGHTING);
	// Habilita a luz de número 0
	glEnable(GL_LIGHT0);
	// Habilita o depth-buffering
	glEnable(GL_DEPTH_TEST);
}

//Função de Saida
void end_program(){
	objs3d_delete(objs);
	exit(0);
}
